Conversation
| @@ -0,0 +1,95 @@ | |||
| package kyo | |||
|
|
|||
| class GuardTest extends Test: | |||
There was a problem hiding this comment.
Can you add tests with multiple effects? It'd be nice to have scenarios where only part of the effects are guarded
There was a problem hiding this comment.
This approach does not support masking specific effects within a larger effect intersection. I added a test with multiple effects though.
| * @see | ||
| * [[Guard]] | ||
| */ | ||
| sealed trait Guarded[S] extends ArrowEffect[[A] =>> A < S, Id] |
There was a problem hiding this comment.
this is quite useful! would it cover the Abort case as well? I think not because of the erased tags, right?
There was a problem hiding this comment.
Maybe Mask for the name could work better? It seems to express well what the effect does since it masks the effect tags. We have fiber.mask but it's a rare low-level operation so the name isn't use yet
There was a problem hiding this comment.
Can't cover the Abort case because it cannot distinguish between different effect types included in an effect intersection. I changed Guard to Mask though.
| * @tparam S | ||
| * The effect type that can be guarded | ||
| */ | ||
| sealed class Guard[S](using tag: Tag[Guarded[S]]): |
There was a problem hiding this comment.
could we avoid allocating this class? I think something like this could work:
opaque type Guard[S] = Unit
object Guard:
extension [S](self: Guard[S])
def apply[A](effect: A < S)(using CanGuard, Frame, Tag[Guard[S]]): A < Guarded[S]| * A new effect with its effect intersection protected from handling | ||
| */ | ||
| def apply[A](effect: A < S)(using CanGuard, Frame): A < Guarded[S] = | ||
| ArrowEffect.suspend[A](tag, effect) |
There was a problem hiding this comment.
Not only this approach composes better and has ContextEffect support but it's also is cheaper than the version I had suggested! My version would reinterpret all suspensions within the guarded computation, this version just suspends once 👏
There was a problem hiding this comment.
It suspends once because it guards the full effect. While I find it very, very elegant, I still want to see what happen on the cases we would need.
def apply[A, S2](effect: A < (S & S2))(using CanGuard, Frame): A < (Guarded[S] & S2)(which your version was probably doing)
There was a problem hiding this comment.
Yeah this is the big limitation of my approach. One implication is that you can't fork Guarded effects because it's not possible to construct an Isolate for it.
There was a problem hiding this comment.
Maybe with something that "just" filter/rewrite tags when needed?(that would be a change in the kernel to pass an actual Mask/Guard on tags)
There was a problem hiding this comment.
The big problem is that ContextEffect suspensions are simply KyoDefer instances without any tags identifying the effect type, so it's not possible to identify and mask them. To support a more general masking effect, we would need to allow including tags on KyoDefer, or add a new Kyo subtype specifically for context effects.
…t; zero allocation masker
|
Changed |
|
If I understood correctly, the next kernel will support that! Should we close it? |
Addresses #1387 and #1381
Problem
Writing functions with generic effect types is dangerous when consuming generic effects and composing them effects that need to be handled within the scope of the function. For instance, introducing
Scopein composition with some generic effect and then handling the scope will handle anyScopesuspensions that may exist in the generic effect. Similarly, handlingAborteffects for common public error types likeThrowableorClosedwill potentially handle those failures in generic effects without intending to, leading to undefined behavior.Solution
Guarded[S]which protectsSfrom being handled.Guarded[S]is just a suspendedA < Sthat can be trivially unsuspended.Guard[S]utility which liftsA < StoA < Guarded[S]Guard[S]can only be used within a context provided byGuard.use, which runsGuarded[S]back toSso thatGuarded[S]can never escape the scope in which its used. This means you can use it within a generic function without having to worry about accidentally handling aGuarded[X]introduced elsewhere.Notes
Closederror propagation #1393 introducesAbort.Maskto solve this problem.ArrowEffectwhich seems too restrictive to me.Guarded[S]is limited by the fact that it has noIsolatewhich means it can't be used in generic concurrent processes. It would be possible to derive anIsolateifGuardedcould isolate an effect for protection from an intersection, which the version in this PR cannot do.